package com.tianji.learning.service.impl;

import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.InfoContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.common.constants.RedisConstants.POINT_BOARD_KEY_PREFIX;
import static com.tianji.common.utils.DateUtils.DATE_SUFFIX_FORMATTER;
import static com.tianji.learning.constants.Constants.POINTS_BOARD_TABLE_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author LYQ
 * @since 2024-07-01
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final StringRedisTemplate redisTemplate;
    private final UserClient userClient;

    /**
     * 查询排行榜信息
     *
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO queryBoardsPage(PointsBoardQuery query) {
        //1、判断是否为当前赛季
        boolean isCurrent = query.getSeason() == null || query.getSeason() == 0;

        //组装redis key（zset）
        LocalDateTime now = LocalDateTime.now();
        String dateStr = now.format(DATE_SUFFIX_FORMATTER);
        String key = POINT_BOARD_KEY_PREFIX + dateStr;
        //2、查询当前用户个人排名信息
        PointsBoard pointsBoard = isCurrent ? queryCurrentBoard(key) : queryHistoryBoard(query.getSeason());

        //3、查询前100名排行信息
        List<PointsBoard> pointsBoardList = isCurrent ? queryCurrentBoardList(key, query.getPageNo(), query.getPageSize()) : queryHistoryBoardList(query);
        PointsBoardVO vo = new PointsBoardVO();

        //4、建立Vo并封装个人信息
        vo.setPoints(pointsBoard.getPoints());
        vo.setRank(pointsBoard.getRank());
        if (ObjectUtils.isEmpty(pointsBoardList)) {
            return vo;
        }
        Set<Long> userIds = pointsBoardList.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userInfoList = userClient.queryUserByIds(userIds);
        Map<Long, String> userInfoMap = new HashMap<>(userInfoList.size());
        if (ObjectUtils.isNotEmpty(userInfoList)) {
            userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        }
        //4、封装voItemList并返回
        List<PointsBoardItemVO> boardList = new ArrayList<>(pointsBoardList.size());
        for (PointsBoard board : pointsBoardList) {
            PointsBoardItemVO itemvo = new PointsBoardItemVO();
            itemvo.setPoints(board.getPoints());
            itemvo.setRank(board.getRank());
            itemvo.setName(userInfoMap.getOrDefault(board.getUserId(), "未知用户"));
            boardList.add(itemvo);
        }
        vo.setBoardList(boardList);
        return vo;
    }

    /**
     * 根据赛季Id创建历史赛季排行榜分表
     *
     * @param seasonId
     */
    @Override
    public void createPointsBoardTable(Integer seasonId) {
        getBaseMapper().createPointsBoardTable(POINTS_BOARD_TABLE_PREFIX + seasonId);
    }

    /**
     * 查询当前赛季排行榜信息
     *
     * @param key
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
        //1、从redis中分页查询排行榜信息
        //        0 - 9 10 - 19
        int from = (pageNo - 1) * pageSize;
        int to = from + pageSize - 1;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, from, to);
        if (ObjectUtils.isEmpty(typedTuples)) {
            return null;
        }
        //遍历typedTuples,封装成PointsBoard
        int rank = from + 1;
        List<PointsBoard> list = new ArrayList<>(typedTuples.size());
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            PointsBoard pointsBoard = new PointsBoard();
            if (typedTuple.getScore()!= null && typedTuple.getValue() != null){
                pointsBoard.setPoints(typedTuple.getScore().intValue());
                pointsBoard.setUserId(Long.valueOf(typedTuple.getValue()));
                pointsBoard.setRank(rank++);
            }
            list.add(pointsBoard);
        }
        return list;
    }

    /**
     * 查询当前赛季用户个人排行榜信息
     *
     * @param key
     * @return
     */
    private PointsBoard queryCurrentBoard(String key) {
        //1、绑定key
        BoundZSetOperations<String, String> bound = redisTemplate.boundZSetOps(key);
        //2、查询当前用户Id
        Long userId = UserContext.getUser();
        //3、获取分数和排名
        Double score = bound.score(userId.toString());
        Long rank = bound.reverseRank(userId.toString());
        //4、封装PointsBoard
        PointsBoard pointsBoard = new PointsBoard();

        pointsBoard.setUserId(userId);
        pointsBoard.setPoints(score == null ? 0 : score.intValue());
        pointsBoard.setRank(rank == null ? 0 : rank.intValue() + 1);
        return pointsBoard;
    }

    /**
     * 查询个人历史排行信息
     *
     * @param season
     * @return
     */
    private PointsBoard queryHistoryBoard(Long season) {
        //1、通过seasonId拼接粒式排行榜表名
        String seasonTableName = POINTS_BOARD_TABLE_PREFIX + season;
        //2、将赛季表名存入threadLocal对象中
        InfoContext.setInfo(seasonTableName);
        //3、mybatisPlus通过定义好的动态表名拦截器修改查询对应的表
        PointsBoard pointsBoard = this.lambdaQuery()
                .eq(PointsBoard::getUserId, UserContext.getUser())
                .one();
        //4、处理vo
        if (ObjectUtils.isEmpty(pointsBoard)) {
            pointsBoard= new PointsBoard();
            pointsBoard.setUserId(UserContext.getUser());
            pointsBoard.setPoints(0);
            pointsBoard.setRank(0);
        }else {
            pointsBoard.setPoints(pointsBoard.getPoints() == null ? 0 : pointsBoard.getPoints());
            pointsBoard.setRank(pointsBoard.getId().intValue());
        }
        pointsBoard.setId(null);
        //5、返回vo
        return pointsBoard;
    }

    /**
     * 查询历史赛季排行榜信息
     *
     * @param query
     * @return
     */
    private List<PointsBoard> queryHistoryBoardList(PointsBoardQuery query) {
        //1、通过seasonId拼接粒式排行榜表名
        String seasonTableName = POINTS_BOARD_TABLE_PREFIX + query.getSeason();
        //2、将赛季表名存入threadLocal对象中
        InfoContext.setInfo(seasonTableName);
        //3、mybatisPlus通过定义好的动态表名拦截器修改查询对应的表
        List<PointsBoard> pageResult = this.lambdaQuery()
                .page(query.toMpPage()).getRecords();
        if (ObjectUtils.isEmpty(pageResult)){
            return null;
        }
        List<PointsBoard> volist = pageResult.stream().map(pointsBoard -> {
            pointsBoard.setPoints(pointsBoard.getPoints() == null ? 0 : pointsBoard.getPoints());
            pointsBoard.setRank(pointsBoard.getId().intValue());
            pointsBoard.setId(null);
            return pointsBoard;
        }).collect(Collectors.toList());
        return volist;
    }
}
